home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / Audio / Patchmix / Source / Subprocess.m < prev    next >
Text File  |  1992-07-31  |  8KB  |  332 lines

  1. /*
  2.     Subprocess.m    (v10)
  3.     by Charles L. Oei
  4.     pty support by Joe Freeman
  5.     Subprocess Example, Release 2.0
  6.     NeXT Computer, Inc. 
  7.  
  8.     You may freely copy, distribute and reuse the code in this example.
  9.     NeXT disclaims any warranty of any kind, expressed or implied, as to
  10.     its fitness for any particular use.
  11. */
  12.  
  13. #import "Subprocess.h"
  14. // #import <sgtty.h>    // needed to compile under Release 1.0
  15. #import <appkit/nextstd.h>
  16. #import <appkit/Application.h>
  17. #import <appkit/Panel.h>
  18.  
  19. #define    PTY_TEMPLATE "/dev/pty??"
  20. #define    PTY_LENGTH 11
  21.  
  22. static void showError();
  23.  
  24.  
  25. /*==========================================================
  26.  *
  27.  * Private Instance Methods
  28.  *
  29.  *==========================================================*/
  30.  
  31. @interface Subprocess(Private)
  32. - childDidExit;
  33. - fdHandler:(int)theFd;
  34. @end
  35.  
  36. @implementation Subprocess(Private)
  37.  
  38. - childDidExit
  39.     // cleanup after a child process exits
  40. {  
  41.     if (childPid)
  42.     {
  43.     DPSRemoveFD(fromChild);
  44.     close(fromChild);
  45.     fclose(fpToChild);
  46.     childPid=0;    // specify that child is dead
  47.     //if (delegate && [delegate respondsTo:@selector(subprocessDone)])
  48.        // [delegate perform:@selector(subprocessDone)];
  49.     }
  50.     return self;
  51. }
  52.  
  53. - fdHandler:(int)theFd
  54.     // DPS handler for output from subprocess
  55. {
  56.     if (((bufferCount = read(theFd, outputBuffer, BUFFERSIZE-1)) < 0) ||
  57.       (!bufferCount))
  58.     {
  59.     [self childDidExit];
  60.     return self;
  61.     }
  62.     outputBuffer[bufferCount] = '\0';
  63.     if (delegate && [delegate respondsTo:@selector(subprocessOutput:)])
  64.     [delegate perform:@selector(subprocessOutput:)
  65.           with:(void *)&outputBuffer];
  66.     return self;
  67. }
  68.  
  69. @end
  70.  
  71.  
  72. /*==========================================================
  73.  *
  74.  * Private Utility Routines
  75.  *
  76.  *==========================================================*/
  77.  
  78. static void
  79. showError (const char *errorString, id theDelegate)
  80.     // ensure errors never get dropped on the floor
  81. {
  82.     if (theDelegate && [theDelegate respondsTo:@selector(subprocessError:)])
  83.     [theDelegate
  84.         perform:@selector(subprocessError:)
  85.         with:(void *)errorString];
  86.     else if (NXApp)    // no delegate, but we're running w/in an App
  87.     NXRunAlertPanel(0, errorString, 0, 0, 0);
  88.     else
  89.     perror(errorString);
  90. }
  91.  
  92. static void
  93. fdHandler (int theFd, id self)
  94.     // DPS handler for output from subprocess
  95. {
  96.     [self fdHandler:theFd];
  97. }
  98.  
  99. static void
  100. getptys (int *master, int *slave)
  101.     // attempt to setup the ptys
  102. {
  103.     char device[PTY_LENGTH];
  104.     char *block, *num;
  105.     char *blockLoc; // specifies the location of block for the device string
  106.     char *numLoc; // specifies the pty name with the digit ptyxD
  107.     char *msLoc; // specifies the master (ptyxx) or slave (ttyxx)
  108.     
  109.  
  110.     struct sgttyb setp =
  111.         {B9600, B9600, (char)0x7f, (char)0x15, (CRMOD|ANYP)};
  112.     struct tchars setc =
  113.         {CINTR, CQUIT, CSTART, CSTOP, CEOF, CBRK};
  114.     struct ltchars sltc =
  115.         {CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT};
  116.     int lset =
  117.         (LCRTBS|LCRTERA|LCRTKIL|LCTLECH|LPENDIN|LDECCTQ);
  118.     int setd = NTTYDISC;
  119.     
  120.  
  121.     strcpy(device, PTY_TEMPLATE); // string constants are notwritable
  122.     blockLoc = &device[ strlen("/dev/pty") ];
  123.     numLoc = &device[ strlen("/dev/pty?") ];
  124.     msLoc = &device[ strlen("/dev/") ];
  125.     for (block = "pqrs"; *block; block++)
  126.     {
  127.                 *blockLoc = *block;
  128.                 for (num = "0123456789abcdef"; *num; num++)
  129.                 {
  130.                         *numLoc = *num;
  131.                         *master = open(device, O_RDWR);
  132.                         if (*master >= 0)
  133.                         {
  134.                                 *msLoc = 't';
  135.                                 *slave = open(device, O_RDWR);
  136.                                 if (*slave >= 0)
  137.                                 {
  138.                                      (void) ioctl(*slave, TIOCSETP,(char *)&setp);
  139.                                      (void) ioctl(*slave, TIOCSETC,(char *)&setc);
  140.                                      (void) ioctl(*slave, TIOCSETD, (char  
  141. *)&setd);
  142.                                      (void) ioctl(*slave, TIOCSLTC, (char  
  143. *)&sltc);
  144.                                      (void) ioctl(*slave, TIOCLSET, (char  
  145. *)&lset);
  146.                                      return;
  147.                                 }
  148.                                 else
  149.                                 {
  150.                                         *msLoc = 'p';
  151.                                 }
  152.                         }
  153.                 } /* hunting through a bank of ptys */
  154.     } /* hunting through blocks of ptys in all the right places */
  155.     *master = -1;
  156.     *slave = -1;
  157. }
  158.  
  159. @implementation Subprocess
  160.  
  161. /*==========================================================
  162.  *
  163.  * Public Instance Methods
  164.  *
  165.  *==========================================================*/
  166.  
  167. - init:(const char *)subprocessString
  168.     // a cover for the below withDelegate:nil, andPtySupport:NO, andStdErr:YES
  169. {
  170.     return
  171.     [self
  172.         init:subprocessString
  173.         withDelegate:nil
  174.         andPtySupport:NO
  175.         andStdErr:YES];
  176. }
  177.  
  178. - init:(const char *)subprocessString
  179.     withDelegate:theDelegate
  180.     andPtySupport:(BOOL)wantsPty
  181.     andStdErr:(BOOL)wantsStdErr
  182.     // initializes an instance of Subprocess and corresponding UNIX process
  183. {
  184.     int pipeTo[2];        // for non-Pty support
  185.     int pipeFrom[2];
  186.     int    tty, numFds, fd;    // for temporary use
  187.     int processGroup;
  188.     int pidChild;        // needed because childPid does not exist
  189.                 // until Subprocess is instantiated
  190.  
  191.     if (wantsPty)
  192.     {
  193.         tty = open("/dev/tty", O_RDWR);
  194.     getptys(&masterPty,&slavePty);
  195.     if (masterPty <= 0 || slavePty <= 0)
  196.     {
  197.         showError("Error grabbing ptys for subprocess.", theDelegate);
  198.         return self;
  199.     }
  200.     // remove the controlling tty if launched from a shell,
  201.     // but not Workspace;
  202.     // so that we have job control over the parent application in shell
  203.     // and so that subprocesses can be restarted in Workspace
  204.     if  ((tty<0) && ((tty = open("/dev/tty", 2))>=0))
  205.     {
  206.         ioctl(tty, TIOCNOTTY, 0);
  207.         close(tty);
  208.     }
  209.     }
  210.     else
  211.     {
  212.     if (pipe(pipeTo) < 0 || pipe(pipeFrom) < 0)
  213.     {
  214.         showError("Error starting UNIX pipes to subprocess.", theDelegate);
  215.         return self;
  216.     }
  217.     }
  218.     
  219.     switch (pidChild = vfork())
  220.     {
  221.     case -1:    // error
  222.     showError("Error starting UNIX vfork of subprocess.", theDelegate);
  223.     return self;
  224.  
  225.     case 0:    // child
  226.     if (wantsPty)
  227.     {
  228.         dup2(slavePty, 0);
  229.         dup2(slavePty, 1);
  230.         if (wantsStdErr)
  231.         dup2(slavePty, 2);
  232.     }
  233.     else
  234.     {
  235.         dup2(pipeTo[0], 0);
  236.         dup2(pipeFrom[1], 1);
  237.         if (wantsStdErr)
  238.         dup2(pipeFrom[1], 2);
  239.     }
  240.     
  241.     numFds = getdtablesize();
  242.     for (fd=3; fd<numFds; fd++)
  243.         close(fd);
  244.  
  245.     processGroup = getpid();
  246.     ioctl(0, TIOCSPGRP, (char *)&processGroup);
  247.     setpgrp (0, processGroup);
  248.     
  249.     // we exec a /bin/sh so that cmds are easier to specify for the user
  250.     execl("/bin/sh", "sh", "-c", subprocessString, 0);
  251.     perror("vfork (child)"); // should never gets here tho
  252.     exit(1);
  253.  
  254.     default:    // parent
  255.     [self setDelegate:theDelegate];
  256.     childPid = pidChild;
  257.  
  258.     if (wantsPty)
  259.     {
  260.         close(slavePty);
  261.         
  262.         fpToChild = fdopen(masterPty, "w");
  263.         fromChild = masterPty;
  264.     }
  265.     else
  266.     {
  267.         close(pipeTo[0]);
  268.         close(pipeFrom[1]);
  269.     
  270.         fpToChild = fdopen(pipeTo[1], "w");
  271.         fromChild = pipeFrom[0];
  272.     }
  273.  
  274.     setbuf(fpToChild, NULL);
  275.     DPSAddFD(
  276.         fromChild,
  277.         (DPSFDProc)fdHandler,
  278.         (id)self,
  279.         NX_MODALRESPTHRESHOLD+1);
  280.     return self;
  281.     }
  282. }
  283.  
  284. - send:(const char *)string withNewline:(BOOL)wantNewline
  285. {
  286.     fputs(string, fpToChild);
  287.     //(fpToChild)->_ptr= string;
  288.     //write(stdout,(char *)(fpToChild)->_ptr,sizeof(string));
  289.     
  290. //printf("data %d\n %s\n %s\n %d\n %d\n",(fpToChild)->_cnt,(fpToChild)->_ptr,(fpToChild)->_base,(fpToChild)->_bufsiz,fpToChild->_flag);
  291.     if (wantNewline)
  292.         fputc('\n', fpToChild);
  293.     return self;
  294. }
  295.  
  296.  
  297. - send:(const char *)string
  298. {
  299.     [self send:string withNewline:YES];
  300.     return self;
  301. }
  302.  
  303. - terminateInput
  304.     // effectively sends an EOF to the child process stdin
  305. {      
  306.     fclose(fpToChild);
  307.     return self;
  308. }
  309.  
  310. - terminate:sender
  311. {  
  312.     if (childPid)
  313.     {
  314.     kill(childPid+1, SIGTERM);
  315.     [self childDidExit];
  316.     }
  317.     return self;
  318. }
  319.  
  320. - setDelegate:anObject
  321. {
  322.     delegate = anObject;
  323.     return self;
  324. }
  325.  
  326. - delegate
  327. {
  328.     return delegate;
  329. }
  330.  
  331. @end
  332.